package drone.basis.util ;

import java.io.ByteArrayInputStream ;
import java.io.File ;
import java.net.URL ;
import java.security.KeyStore ;
import java.security.SecureRandom ;
import java.security.cert.CertificateException ;
import java.security.cert.X509Certificate ;
import java.util.ArrayList ;
import java.util.LinkedList ;
import java.util.List ;
import java.util.Map ;
import java.util.Objects ;

import javax.net.ssl.KeyManagerFactory ;
import javax.net.ssl.SSLContext ;
import javax.net.ssl.TrustManager ;
import javax.net.ssl.X509TrustManager ;

import org.apache.http.Consts ;
import org.apache.http.Header ;
import org.apache.http.HeaderElement ;
import org.apache.http.HttpEntity ;
import org.apache.http.HttpHeaders ;
import org.apache.http.NameValuePair ;
import org.apache.http.client.config.RequestConfig ;
import org.apache.http.client.entity.GzipDecompressingEntity ;
import org.apache.http.client.entity.UrlEncodedFormEntity ;
import org.apache.http.client.methods.CloseableHttpResponse ;
import org.apache.http.client.methods.HttpGet ;
import org.apache.http.client.methods.HttpPost ;
import org.apache.http.client.utils.URIBuilder ;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory ;
import org.apache.http.entity.ContentType ;
import org.apache.http.entity.StringEntity ;
import org.apache.http.entity.mime.MultipartEntityBuilder ;
import org.apache.http.impl.client.CloseableHttpClient ;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler ;
import org.apache.http.impl.client.HttpClients ;
import org.apache.http.message.BasicNameValuePair ;
import org.apache.http.util.EntityUtils ;

public class HttpUtil
{

	private static final Integer OVERTIME = 60 * 1000 ;

	private static final Integer RETRYTIMES = 3 ;

	public static String get (
			String urlPath
	) throws Exception
	{
		return get ( urlPath , null ) ;
	}

	public static String get (
			String urlPath ,
			Map < String , String > paramsMap
	) throws Exception
	{
		return get ( urlPath , paramsMap , null , OVERTIME ) ;
	}

	public static String get (
			String urlPath ,
			Map < String , String > paramsMap ,
			Map < String , String > headerMap
	) throws Exception
	{
		return get ( urlPath , paramsMap , headerMap , OVERTIME ) ;
	}

	public static String get (
			String urlPath ,
			Map < String , String > paramsMap ,
			Map < String , String > headerMap ,
			Integer overTime
	) throws Exception
	{
		URIBuilder uri = new URIBuilder ( urlPath ) ;
		if ( ! Objects.isNull ( paramsMap ) )
		{
			List < NameValuePair > list = new LinkedList < NameValuePair > ( ) ;
			for ( Map.Entry < String , String > e : paramsMap.entrySet ( ) )
			{
				list.add ( new BasicNameValuePair ( e.getKey ( ) , e.getValue ( ) ) ) ;
			}

			uri.setParameters ( list ) ;
		}
		CloseableHttpClient httpClient = HttpClients.createDefault ( ) ;
		HttpGet httpGet = new HttpGet ( uri.build ( ) ) ;
		RequestConfig requestConfig = RequestConfig.custom ( ).setSocketTimeout ( overTime ).setConnectTimeout ( overTime ).setConnectionRequestTimeout ( overTime ).build ( ) ;// 设置请求和传输超时时间
		httpGet.setConfig ( requestConfig ) ;
		if ( headerMap != null )
		{
			for ( Map.Entry < String , String > entry : headerMap.entrySet ( ) )
			{
				httpGet.addHeader ( entry.getKey ( ) , entry.getValue ( ) ) ;
			}
		}
		CloseableHttpResponse httpResponse = httpClient.execute ( httpGet ) ;
		HttpEntity httpEntity = httpResponse.getEntity ( ) ;
		Header ceheader = httpEntity.getContentEncoding ( ) ;
		if ( ceheader != null )
		{
			for ( HeaderElement element : ceheader.getElements ( ) )
			{
				if ( "gzip".equalsIgnoreCase ( element.getName ( ) ) )
				{
					httpEntity = new GzipDecompressingEntity ( httpResponse.getEntity ( ) ) ;
					break ;
				}
			}
		}
		String result = null ;
		if ( httpEntity != null )
		{
			result = EntityUtils.toString ( httpEntity , "UTF-8" ) ;
		}
		httpResponse.close ( ) ;
		httpClient.close ( ) ;
		return result ;
	}

	public static String post (
			String urlPath
	) throws Exception
	{
		return post ( urlPath , null , null ) ;
	}

	public static String post (
			String urlPath ,
			Map < String , String > paramsMap
	) throws Exception
	{
		return post ( urlPath , paramsMap , null , OVERTIME ) ;
	}

	public static String post (
			String urlPath ,
			Map < String , String > paramsMap ,
			Map < String , String > headerMap
	) throws Exception
	{
		return post ( urlPath , paramsMap , headerMap , OVERTIME ) ;
	}

	public static String post (
			String urlPath ,
			Map < String , String > paramsMap ,
			Map < String , String > headerMap ,
			Integer overTime
	) throws Exception
	{
		return post ( urlPath , paramsMap , headerMap , overTime , commonFactory ( ) ) ;
	}

	public static String post (
			String urlPath ,
			Map < String , String > paramsMap ,
			Map < String , String > headerMap ,
			Integer overTime ,
			SSLConnectionSocketFactory sslConnectionSocketFactory
	) throws Exception
	{
		if ( StringUtil.isEmpty ( urlPath ) )
		{
			throw new Exception ( "url is null" ) ;
		}

		CloseableHttpClient httpClient = null ;
		if ( urlPath.startsWith ( "https" ) )
		{
			httpClient = HttpClients.custom ( ).setSSLSocketFactory ( sslConnectionSocketFactory ).setRetryHandler ( new DefaultHttpRequestRetryHandler ( RETRYTIMES , true ) ).build ( ) ;
		} else
		{
			httpClient = HttpClients.custom ( ).setRetryHandler ( new DefaultHttpRequestRetryHandler ( RETRYTIMES , true ) ).build ( ) ;
		}

		HttpPost httpPost = new HttpPost ( urlPath ) ;
		RequestConfig requestConfig = RequestConfig.custom ( ).setSocketTimeout ( overTime ).setConnectTimeout ( overTime ).setConnectionRequestTimeout ( overTime ).build ( ) ;// 设置请求和传输超时时间
		httpPost.setConfig ( requestConfig ) ;

		List < BasicNameValuePair > formparams = new ArrayList < BasicNameValuePair > ( ) ;
		if ( paramsMap != null )
		{
			for ( Map.Entry < String , String > entry : paramsMap.entrySet ( ) )
			{
				formparams.add ( new BasicNameValuePair ( entry.getKey ( ) , entry.getValue ( ) ) ) ;
			}
		}

		httpPost.setEntity ( new UrlEncodedFormEntity ( formparams , "UTF-8" ) ) ;

		if ( headerMap != null )
		{
			for ( Map.Entry < String , String > entry : headerMap.entrySet ( ) )
			{
				httpPost.addHeader ( entry.getKey ( ) , entry.getValue ( ) ) ;
			}
		}

		CloseableHttpResponse httpResponse = httpClient.execute ( httpPost ) ;
		HttpEntity httpEntity = httpResponse.getEntity ( ) ;
		String result = null ;
		if ( httpEntity != null )
		{
			result = EntityUtils.toString ( httpEntity , "UTF-8" ) ;
		}
		httpResponse.close ( ) ;
		httpClient.close ( ) ;
		return result ;
	}

	public static String upload (
			String urlPath ,
			String uploadKey ,
			File uploadFile
	) throws Exception
	{
		return upload ( urlPath , uploadKey , uploadFile , null , null ) ;
	}

	public static String upload (
			String urlPath ,
			String uploadKey ,
			File uploadFile ,
			Map < String , String > paramsMap
	) throws Exception
	{
		return upload ( urlPath , uploadKey , uploadFile , paramsMap , null , OVERTIME ) ;
	}

	public static String upload (
			String urlPath ,
			String uploadKey ,
			File uploadFile ,
			Map < String , String > paramsMap ,
			Map < String , String > headerMap
	) throws Exception
	{
		return upload ( urlPath , uploadKey , uploadFile , paramsMap , headerMap , OVERTIME ) ;
	}

	public static String upload (
			String urlPath ,
			String uploadKey ,
			File uploadFile ,
			Map < String , String > paramsMap ,
			Map < String , String > headerMap ,
			Integer overTime
	) throws Exception
	{
		return upload ( urlPath , uploadKey , uploadFile , paramsMap , headerMap , overTime , commonFactory ( ) ) ;
	}

	public static String upload (
			String urlPath ,
			String uploadKey ,
			File uploadFile ,
			Map < String , String > paramsMap ,
			Map < String , String > headerMap ,
			Integer overTime ,
			SSLConnectionSocketFactory sslConnectionSocketFactory
	) throws Exception
	{
		if ( StringUtil.isEmpty ( urlPath ) )
		{
			throw new Exception ( "url is null" ) ;
		}

		CloseableHttpClient httpClient = null ;
		if ( urlPath.startsWith ( "https" ) )
		{
			httpClient = HttpClients.custom ( ).setSSLSocketFactory ( sslConnectionSocketFactory ).setRetryHandler ( new DefaultHttpRequestRetryHandler ( RETRYTIMES , true ) ).build ( ) ;
		} else
		{
			httpClient = HttpClients.custom ( ).setRetryHandler ( new DefaultHttpRequestRetryHandler ( RETRYTIMES , true ) ).build ( ) ;
		}

		HttpPost httpPost = new HttpPost ( urlPath ) ;
		RequestConfig requestConfig = RequestConfig.custom ( ).setSocketTimeout ( overTime ).setConnectTimeout ( overTime ).setConnectionRequestTimeout ( overTime ).build ( ) ;// 设置请求和传输超时时间
		httpPost.setConfig ( requestConfig ) ;

		MultipartEntityBuilder builder = MultipartEntityBuilder.create ( ) ;

		if ( ! StringUtil.isEmpty ( uploadKey ) && ! Objects.isNull ( uploadFile ) )
		{
			builder.addBinaryBody ( uploadKey , uploadFile , ContentType.DEFAULT_BINARY , uploadFile.getName ( ) ) ;
		}

		if ( headerMap != null )
		{
			for ( Map.Entry < String , String > entry : headerMap.entrySet ( ) )
			{
				httpPost.addHeader ( entry.getKey ( ) , entry.getValue ( ) ) ;
			}
		}

		if ( paramsMap != null )
		{
			for ( Map.Entry < String , String > entry : paramsMap.entrySet ( ) )
			{

				if ( ! StringUtil.isEmpty ( entry.getKey ( ) ) )
				{
					builder.addTextBody ( StringUtil.space ( entry.getKey ( ) ) , StringUtil.space ( entry.getValue ( ) ) , ContentType.TEXT_PLAIN ) ;
				}
			}
		}

		HttpEntity entity = builder.build ( ) ;
		httpPost.setEntity ( entity ) ;

		CloseableHttpResponse httpResponse = httpClient.execute ( httpPost ) ;
		HttpEntity httpEntity = httpResponse.getEntity ( ) ;
		String result = null ;
		if ( httpEntity != null )
		{
			result = EntityUtils.toString ( httpEntity , "UTF-8" ) ;
		}
		httpResponse.close ( ) ;
		httpClient.close ( ) ;
		return result ;
	}

	public static String redirect (
			String urlPath
	) throws Exception
	{
		return redirect ( urlPath , null , null ) ;
	}

	public static String redirect (
			String urlPath ,
			Map < String , String > paramsMap ,
			Map < String , String > headerMap
	) throws Exception
	{
		return redirect ( urlPath , paramsMap , headerMap , OVERTIME ) ;
	}

	public static String redirect (
			String urlPath ,
			Map < String , String > paramsMap ,
			Map < String , String > headerMap ,
			Integer overTime
	) throws Exception
	{
		return redirect ( urlPath , paramsMap , headerMap , overTime , commonFactory ( ) ) ;
	}

	public static String redirect (
			String urlPath ,
			Map < String , String > paramsMap ,
			Map < String , String > headerMap ,
			Integer overTime ,
			SSLConnectionSocketFactory sslConnectionSocketFactory
	) throws Exception
	{

		if ( StringUtil.isEmpty ( urlPath ) )
		{
			throw new Exception ( "url is null" ) ;
		}

		CloseableHttpClient httpClient = null ;
		if ( urlPath.startsWith ( "https" ) )
		{
			httpClient = HttpClients.custom ( ).setSSLSocketFactory ( sslConnectionSocketFactory ).setRetryHandler ( new DefaultHttpRequestRetryHandler ( RETRYTIMES , true ) ).build ( ) ;
		} else
		{
			httpClient = HttpClients.custom ( ).setRetryHandler ( new DefaultHttpRequestRetryHandler ( RETRYTIMES , true ) ).build ( ) ;
		}
		HttpPost httpPost = new HttpPost ( urlPath ) ;
		RequestConfig requestConfig = RequestConfig.custom ( ).setSocketTimeout ( overTime ).setConnectTimeout ( overTime ).setConnectionRequestTimeout ( overTime ).build ( ) ;// 设置请求和传输超时时间
		httpPost.setConfig ( requestConfig ) ;
		List < BasicNameValuePair > formparams = new ArrayList < BasicNameValuePair > ( ) ;
		if ( paramsMap != null )
		{
			for ( Map.Entry < String , String > entry : paramsMap.entrySet ( ) )
			{
				formparams.add ( new BasicNameValuePair ( entry.getKey ( ) , entry.getValue ( ) ) ) ;
			}
		}
		httpPost.setEntity ( new UrlEncodedFormEntity ( formparams , "UTF-8" ) ) ;
		if ( headerMap != null )
		{
			for ( Map.Entry < String , String > entry : headerMap.entrySet ( ) )
			{
				httpPost.setHeader ( entry.getKey ( ) , entry.getValue ( ) ) ;
			}
		}
		CloseableHttpResponse httpResponse = httpClient.execute ( httpPost ) ;
		URL redirectUrl = new URL ( httpResponse.getHeaders ( "location" ) [ 0 ].getValue ( ) ) ;
		String result = redirectUrl.getQuery ( ) ;
		httpResponse.close ( ) ;
		httpClient.close ( ) ;
		return result ;
	}

	public static String json (
			String urlPath
	) throws Exception
	{
		return json ( urlPath , "{}" ) ;
	}

	public static String json (
			String urlPath ,
			String jsonParam
	) throws Exception
	{
		return json ( urlPath , jsonParam , null , OVERTIME , commonFactory ( ) ) ;
	}

	public static String json (
			String urlPath ,
			String jsonParam ,
			Integer overTime
	) throws Exception
	{
		return json ( urlPath , jsonParam , null , overTime , commonFactory ( ) ) ;
	}

	public static String json (
			String urlPath ,
			String jsonParam ,
			Map < String , String > headerMap
	) throws Exception
	{
		return json ( urlPath , jsonParam , headerMap , OVERTIME , commonFactory ( ) ) ;
	}

	public static String json (
			String urlPath ,
			String jsonParam ,
			Map < String , String > headerMap ,
			Integer overTime
	) throws Exception
	{
		return json ( urlPath , jsonParam , headerMap , overTime , commonFactory ( ) ) ;
	}

	public static String json (
			String urlPath ,
			String jsonParam ,
			Map < String , String > headerMap ,
			Integer overTime ,
			SSLConnectionSocketFactory sslConnectionSocketFactory
	) throws Exception
	{
		if ( StringUtil.isEmpty ( urlPath ) )
		{
			throw new Exception ( "url is null" ) ;
		}

		CloseableHttpClient httpClient = null ;
		if ( urlPath.startsWith ( "https" ) )
		{
			httpClient = HttpClients.custom ( ).setSSLSocketFactory ( sslConnectionSocketFactory ).setRetryHandler ( new DefaultHttpRequestRetryHandler ( RETRYTIMES , true ) ).build ( ) ;
		} else
		{
			httpClient = HttpClients.custom ( ).setRetryHandler ( new DefaultHttpRequestRetryHandler ( RETRYTIMES , true ) ).build ( ) ;
		}
		HttpPost httpPost = new HttpPost ( urlPath ) ;
		RequestConfig requestConfig = RequestConfig.custom ( ).setSocketTimeout ( overTime ).setConnectTimeout ( overTime ).setConnectionRequestTimeout ( overTime ).build ( ) ;// 设置请求和传输超时时间
		httpPost.setConfig ( requestConfig ) ;
		httpPost.setEntity ( new StringEntity ( jsonParam , ContentType.create ( "application/json" , Consts.UTF_8 ) ) ) ;
		httpPost.setHeader ( HttpHeaders.CONTENT_TYPE , "application/json" ) ;
		if ( headerMap != null )
		{
			for ( Map.Entry < String , String > entry : headerMap.entrySet ( ) )
			{
				httpPost.setHeader ( entry.getKey ( ) , entry.getValue ( ) ) ;
			}
		}
		CloseableHttpResponse httpResponse = httpClient.execute ( httpPost ) ;
		HttpEntity httpEntity = httpResponse.getEntity ( ) ;
		String result = null ;
		if ( httpEntity != null )
		{
			result = EntityUtils.toString ( httpEntity , "UTF-8" ) ;
		}
		httpResponse.close ( ) ;
		httpClient.close ( ) ;
		return result ;

	}

	public static String xml (
			String urlPath
	) throws Exception
	{
		return xml ( urlPath , "{}" ) ;
	}

	public static String xml (
			String urlPath ,
			String xmlParam
	) throws Exception
	{
		return xml ( urlPath , xmlParam , OVERTIME ) ;
	}

	public static String xml (
			String urlPath ,
			String xmlParam ,
			Integer overTime
	) throws Exception
	{
		return xml ( urlPath , xmlParam , overTime , commonFactory ( ) ) ;
	}

	public static String xml (
			String urlPath ,
			String xmlParam ,
			Integer overTime ,
			SSLConnectionSocketFactory sslConnectionSocketFactory
	) throws Exception
	{
		if ( StringUtil.isEmpty ( urlPath ) )
		{
			throw new Exception ( "url is null" ) ;
		}

		CloseableHttpClient httpClient = null ;
		if ( urlPath.startsWith ( "https" ) )
		{
			httpClient = HttpClients.custom ( ).setSSLSocketFactory ( sslConnectionSocketFactory ).setRetryHandler ( new DefaultHttpRequestRetryHandler ( RETRYTIMES , true ) ).build ( ) ;
		} else
		{
			httpClient = HttpClients.custom ( ).setRetryHandler ( new DefaultHttpRequestRetryHandler ( RETRYTIMES , true ) ).build ( ) ;
		}
		HttpPost httpPost = new HttpPost ( urlPath ) ;
		RequestConfig requestConfig = RequestConfig.custom ( ).setSocketTimeout ( overTime ).setConnectTimeout ( overTime ).setConnectionRequestTimeout ( overTime ).build ( ) ;// 设置请求和传输超时时间
		httpPost.setConfig ( requestConfig ) ;
		httpPost.setEntity ( new StringEntity ( xmlParam , ContentType.create ( "application/xml" , Consts.UTF_8 ) ) ) ;
		httpPost.setHeader ( HttpHeaders.CONTENT_TYPE , "application/xml" ) ;
		CloseableHttpResponse httpResponse = httpClient.execute ( httpPost ) ;
		HttpEntity httpEntity = httpResponse.getEntity ( ) ;
		String result = null ;
		if ( httpEntity != null )
		{
			result = EntityUtils.toString ( httpEntity , "UTF-8" ) ;
		}
		httpResponse.close ( ) ;
		httpClient.close ( ) ;
		return result ;

	}

	public static SSLConnectionSocketFactory commonFactory ( ) throws Exception
	{

		SSLContext sslContext = SSLContext.getInstance ( "TLSv1.2" ) ;
		X509TrustManager x509TrustManager = new X509TrustManager ( )
		{

			@Override
			public X509Certificate [ ] getAcceptedIssuers ( )
			{
				return null ;
			}

			@Override
			public void checkServerTrusted (
					X509Certificate [ ] chain ,
					String authType
			) throws CertificateException
			{
			}

			@Override
			public void checkClientTrusted (
					X509Certificate [ ] chain ,
					String authType
			) throws CertificateException
			{
			}

		} ;
		sslContext.init ( null , new TrustManager [ ]
		{
				x509TrustManager
		} , null ) ;

		SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory ( sslContext ) ;

		return sslConnectionSocketFactory ;
	}

	public static SSLConnectionSocketFactory assignFactory (
			String certContent ,
			String certPassword
	) throws Exception
	{

		SSLContext sslContext = SSLContext.getInstance ( "TLSv1.2" ) ;
		X509TrustManager x509TrustManager = new X509TrustManager ( )
		{

			@Override
			public X509Certificate [ ] getAcceptedIssuers ( )
			{
				return null ;
			}

			@Override
			public void checkServerTrusted (
					X509Certificate [ ] chain ,
					String authType
			) throws CertificateException
			{
			}

			@Override
			public void checkClientTrusted (
					X509Certificate [ ] chain ,
					String authType
			) throws CertificateException
			{
			}

		} ;

		KeyStore clientStore = KeyStore.getInstance ( "PKCS12" ) ;

		clientStore.load ( new ByteArrayInputStream ( HexUtil.hex2Bytes ( certContent ) ) , certPassword.toCharArray ( ) ) ;
		KeyManagerFactory kmf = KeyManagerFactory.getInstance ( KeyManagerFactory.getDefaultAlgorithm ( ) ) ;
		kmf.init ( clientStore , certPassword.toCharArray ( ) ) ;

		sslContext.init ( kmf.getKeyManagers ( ) , new TrustManager [ ]
		{
				x509TrustManager
		} , new SecureRandom ( ) ) ;

		SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory ( sslContext ) ;

		return sslConnectionSocketFactory ;
	}

}
